;
;	Overlay to ZMP (Z-Modem Program), for the Kaypro CP/M computers.
;		(Z80 SIO and 8116 baudrate generator.)
;
;	Name:  ZMO-KP05.MAC
;
;	Dated: November 25, 1988
;
;	Written by -
;	  Ron Murray, c/o Z-Node 62, 061-9-450-0200, Perth, Western Australia.
;
; 89/04/12 - Modified to ZMP v1.5		- George Conover
;	KP05 - Nov. 24, 88. Updated for ZMP version 1.4 (overlay drive/user
;			equates).  Added conditional to check RTS/CTS signals.
;			Bill Duerr, GEnie ID: B.DUERR
;				    DKUG RBBS [313] 772-0522
;
;	KP04 - Oct. 22, 88. Updated to allow program to be reentered properly,
;			for 8-N-1 only -- still problems with even/odd parity.
;			Bill Duerr, GEnie ID: B.DUERR
;				    DKUG RBBS [313] 772-0522
;
;	KP03 - Oct. 12, 88 Modified to ZMP v1.3 standard rjm
;
;
;    Insert your own code as necessary in this file. Code contained herein
; has been written in Z80 code for use with M80. Once assembled,
; convert to hex with RELHEX and use MLOAD to overlay it over the main
; ZMPX.COM file to produce your very own ZMP.COM.
;
; Notes on modifying this file:
;    Hi-Tech C requires that functions do not change either index register
; (IX or IY). If your overlay requires either of these to be changed, ensure
; they are restored to their original values on return.
;    Since collecting parameters from C functions can be tricky, only change
; the parts marked 'Insert your own code here'. Do NOT modify the jump
; table at the start. Do NOT modify the entry/exit sections of each
; function. Do NOT pass 'GO'. Do NOT collect $200.
;    Apart from defining modem functions, this file also defines terminal
; characteristics. Most have been set up for ADM-3A (with a few of my own
; additions). Modify to suit your own terminal. An inline print routine
; is provided for printing strings in the usual way: usage is
;
;	call	print
;	db	'required string',0
;
;    Don't forget to set your clock speed at the clkspd variable.
;
;    If you find your overlay exceeds the maximum size (currently 0400h),
; you will have to re-compile the whole thing. Good luck. You might try
; informing us if you need to do this: if too many people need to do it, we
; haven't allowed enough room.
;
; Ron Murray 15/8/88
;
;
false	equ	0
true	equ	not false
;
; If the following equate is set to true, code will be added to set
; the RTS modem signal, telling the modem not to send any characters
; till RTS is reset, and also to check CTS if characters may be sent.
; This allows the Kaypro screen to keep up with the characters sent
; over the modem lines at higher speeds.  (The modem must be able to
; recognize RTS and set CTS for this to work.)
;
rtscts	equ	true
;
;Set the following two equates to the drive and user area which will contain
;   ZMP's .OVR files, .CFG file, .FON file and .HLP file.  Set both to zero
;   (null) to locate them on the drive from which ZMP was invoked.

overdrive	equ	'A'	; Drive to find overlay files on ('A'-'P')
overuser	equ	15	; User area to find files

;------------------------------------------------------------------------------

;User-set variables:
clkspd	equ	4		; Processor clock speed in MHz
mspeed	equ	003ch		; Current baud rate: as used by BYE etc
				; This MUST be the same as Mspeed in
				; ZMP.H


dport	equ	04h		; Kaypro SIO data port
sport	equ	dport+2		; Modem control port
mdrcv	equ	01h		; Modem receive ready
mdsnd	equ	04h		; Modem send ready bit
mdtxe	equ	01h		; Modem send buffer empty, holding buffer empty
rsterr	equ	30h		; Reset parity and overrun flags - register 0
reg1ins	equ	0		; No interrupts - register 1
reg3ins	equ	0c1h		; 8 Rx bits, Rx enable
reg4ins	equ	44h		; 16X baud rate, 1 stop bit, no parity
reg5ins	equ	0eah		; 8 Tx bits, Tx enable, RTS, DTR

bauda	equ	00h		; 8116 port for baudrate
rstsio	equ	18h		; Reset the SIO
dcd	equ	08h		; Data Carrier Detect
dtr	equ	80h		; Data Terminal Ready
rts	equ	02h		; Request to send
cts	equ	20h		; Clear to send
brk	equ	10h		; SIO Break
sioerr	equ	70h		; SIO error

userdef	equ	00145h		; origin of this overlay: get this value
				; from the .SYM file produced when ZMP.COM
				; is linked
ovsize	equ	0400h		; max size of this overlay

	org	userdef

esc	equ	1bh
ctrlq	equ	11h
cr	equ	0dh
lf	equ	0ah
bdos	equ	5

;Jump table for the overlay: do NOT change this

jump_tab:
	jp	scrnpr		; screen print
	jp	mrd		; modem read with timeout
	jp	mchin		; get a character from modem
	jp	mchout		; send a character to the modem
	jp	mordy		; test for tx buffer empty
	jp	mirdy		; test for character received
	jp	sndbrk		; send break
	jp	cursadd		; cursor addressing
	jp	cls		; clear screen
	jp	invon		; inverse video on
	jp	invoff		; inverse video off
	jp	hide		; hide cursor
	jp	show		; show cursor
	jp	savecu		; save cursor position
	jp	rescu		; restore cursor position
	jp	mint		; service modem interrupt
	jp	invec		; initialise interrupt vectors
	jp	dinvec		; de-initialise interrupt vectors
	jp	mdmerr		; test uart flags for error
	jp	dtron		; turn DTR on
	jp	dtroff		; turn DTR OFF
	jp	init		; initialise uart
	jp	wait		; wait seconds
	jp	mswait		; wait milliseconds
	jp	userin		; user-defined entry routine
	jp	userout		; user-defined exit routine
	jp	getvars		; get system variables
  	JP	SETPORT		; Set port (0 or 1)

; Spare jumps for compatibility with future versions
	jp	spare		; spare for later use
	jp	spare		; spare for later use
	jp	spare		; spare for later use
	jp	spare		; spare for later use
	jp	spare		; spare for later use
	jp	spare		; spare for later use

;
; Main code starts here
;
codebgn	equ	$
;
;Screen print function
scrnpr:
				; <== Insert your own code here
	call	print
	db	'This function not supported.',cr,lf,0
				; <== End of your own code
spare:
	ret

; User-defined entry routine
userin:
	ret
; User-defined exit routine
userout:			; Leave RTS off
	if	rtscts
	ld	a,05h		; Select register 5
	out	(sport),a	; Send to the status port
	ld	a,(reg5)	; Get register 5 settings
	and	255-rts		; Turn off RTS
	out	(sport),a	; Send to the status port
	endif			; rtscts
	ret

;Get a character from the modem: return in HL
; It is not necessary to test for status
mchin:
;-- Code added specific to Kaypro
	in	a,(dport)	; get the character in A
;
; This routine checks parity, and if parity is not "none", strips high bit.
; This is needed, even if the filter is turned on, because CONNECT message
; is not filtered.
;
	ld	l,a		; Save in L
	ld	a,(parity)	; Check parity
	cp	'N'		; No parity?
	ld	a,l		; Get the character back
	jr	z,mchin1	; Bypass following kludge
	and	7fh
	ld	l,a		; put in HL
mchin1:
	if	rtscts
	ld	a,05h		; Select register 5
	out	(sport),a	; Send to the status port
	ld	a,(reg5)	; Get register 5 settings
	and	255-rts		; Turn off RTS
	out	(sport),a	; Send to the status port
	endif			; rtscts
;-- End of this block of machine specific code
	ld	a,l		; get character from HL
	ld	h,0
	or	a		; set/clear Z
	ret

;Send a character to the modem
mchout:
	ld	hl,2		; Get the character
	add	hl,sp
	ld	a,(hl)		; in A
;-- Code added specific to Kaypro
	out	(dport),a	; Put the character to the serial port
;-- End of this block of machine specific code
	ret			; done

;Test for output ready: return TRUE (1) in HL if ok
mordy:
;-- Code added specific to Kaypro
	ld	hl,0		; Assume not ready
;
	if	rtscts
	ld	a,10h		; Reset register 0
	out	(sport),a	; Send to the status port
	in	a,(sport)	; read status port
	and	cts		; Is clear to send up
	jr	z,mordy1	; Branch if not clear to send
	endif			; rtscts
;
	ld	a,10h		; Reset register 0
	out	(sport),a	; Send to the status port
	in	a,(sport)	; Read status port
	and	mdsnd		; Isolate transmit empty bit
	jr	z,mordy1	; Branch if not ready
	inc	hl		; otherwise set HL to indicate ready
mordy1:
;-- End of this block of machine specific code
	ld	a,l
	or	a		; set/clear Z
	ret

;Test for character at modem: return TRUE (1) in HL if so
mirdy:
;-- Code added specific to Kaypro
	ld	a,05h		; Select register 5
	out	(sport),a	; Send to the status port
	ld	a,(reg5)	; Get register 5 settings
	or	rts		; Turn on RTS
	out	(sport),a	; Send to the status port
	ld	hl,0		; Assume not ready
	ld	a,10h		; Reset register 0
	out	(sport),a	; Send to the status port
	in	a,(sport)	; Get the status
	and	mdrcv		; Isolate receive ready bit
	jr	z,mirdy1	; Branch if not ready
	inc	hl		; otherwise set HL to indicate ready
mirdy1:
;-- End of this block of machine specific code
	ld	a,l
	or	a		; set/clear Z
	ret

;Send a break to the modem: leave empty if your system can't do it
sndbrk:
;-- Code added specific to Kaypro
	ld	a,5		; Select register 5
	out	(sport),a	; Send to the status port
	ld	a,(reg5)	; Get parameters for register 5
	or	brk		; Set the break tone
	out	(sport),a	; Send to the status port
;-- End of this block of machine specific code
	ld	hl,300
	call	waitms		; wait 300 mS
;-- Code added specific to Kaypro
	ld	a,5		; Select register 5
	out	(sport),a	; Send to the status port
	ld	a,(reg5)	; Restore register 5 settings
	out	(sport),a	; Send to the status port
;-- End of this block of machine specific code
	ret

;Test UART flags for error: return TRUE (1) in HL if error
mdmerr:
;-- Code added specific to Kaypro
	ld	hl,0		; Assume no error
	ld	a,011h		; Select read register 1
	out	(sport),a	; Send to the status port
	in	a,(sport)	; Get error status
	and	sioerr		; Mask all but error bits
	ret	z		; Return zeor in HL if no error
	inc	hl		; Otherwise set HL to indicate error
	ret			; Return


;Turn DTR (and optionally RTS) ON.
dtron:
;-- Code added specific to Kaypro
	ld	a,05h		; Select register 5
	out	(sport),a	; Send to the status port
	ld	a,(reg5)	; Get register 5 settings
	out	(sport),a	; Send to the status port
;-- End of this block of machine specific code
	ret

;Turn DTR ( and RTS?) OFF
dtroff:
;-- Code added specific to Kaypro
	ld	a,05h		; Select register 5
	out	(sport),a	; Send to the status port
	ld	a,(reg5)	; Get register 5 settings
	and	255-dtr		; Turn off DTR
	out	(sport),a	; Send to the status port
;-- End of this block of machine specific code
	ret

;Initialise the UART
init:
	ld	hl,2		; get parameters
	add	hl,sp
	ld	a,(hl)		; get lo
	ld	(mspeed),a	; save in baud rate low memory
	inc	hl
	inc	hl		; bump for next
	ld	a,(hl)		; get lo
	and	5Fh		; Convert to upper case
	ld	(parity),a	; parity
	inc	hl
	inc	hl		; bump for next
	ld	a,(hl)		; get lo
	and	0fh		; Make sure binary
	ld	(data),a	; Save data bits
	cp	7		; but 7 if required
	ld	a,reg5ins	; 8 Tx bits, Tx enable, RTS, DTR
	jr	nz,setbs
	ld	a,reg5ins-040h	; Turn bit off
setbs:
	ld	(reg5),a
	inc	hl
	inc	hl		; bump for next
	ld	a,(hl)		; get lo
	and	0fh		; Make sure binary
	ld	(stop),a	; stop bits
				; <== Insert your own code here
;-- Code added specific to Kaypro
	ld	a,10h		; Reset register 0
	out	(sport),a	; Send to the status port
	in	a,(sport)	; read status port
	and	DCD		; Is carrier up
	jr	nz,initr	; Branch if carrier already there
	ld	A,0		; Select register 0
	out	(sport),a	; Send to status port
	ld	a,rstsio	; Reset SIO - 18h
	out	(sport),a	; Send to status port
;
	ld	A,4		; Select register 4
	out	(sport),a	; Send to status port
	ld	a,(parity)	; do parity
	ld	e,003h		; assume even parity
	cp	'E'		; even?
	jr	z,setstop	; yes
	ld	e,001h		; try odd
	cp	'O'
	jr	z,setstop
	ld	e,0		; Indicate no parity
setstop:
	ld	a,(stop)	; set stop bits
	cp	2
	ld	a,reg4ins	; 16X baud rate, 1 stop bit, no parity
	jr	nz,onestop
	or	00ch		; set bits 2 and 3
onestop:
	or	e		; put parity bits in the register
;
	out	(sport),a	; Send to status port
;
	ld	a,3		; Select register 3
	out	(sport),a	; Send to status port
;
	ld	a,(data)	; get data bits parameter field
	cp	7		; is 7 required
	ld	a,reg3ins	; 8 Rx bits, Rx enable
	jr	nz,setbr
	and	07fh
setbr:
	out	(sport),a	; Set Rx bits
	ld	a,5		; Select register 5
	out	(sport),a	; Send to status port
	ld	a,(reg5)	; Get register 5 settings
	out	(sport),a	; Set tx bits
;
	ld	a,1		; Select register 1
	out	(sport),a
	ld	a,reg1ins	; No interrupts
	out	(sport),a	; Send to status port
;
	ld	a,(mspeed)	; set baud rate: get index
	ld	hl,brval
	ld	e,a		; rate to DE
	ld	d,0
	add	hl,de
	ld	a,(hl)		; get baud rate value
	out	(bauda),a
				; <== End of your own code
initr:	ret

;
; Baud rate factors, output to bauda to select baud rate
;
bd10	equ	02h		; 110 baud rate 	0
bd30	equ	05h		; 300 baud rate 	1
bd12	equ	07h		; 1200 baud rate	5
bd24	equ	0ah		; 2400 baud rate	6
bd48	equ	0ch		; 4800 baud rate	7
bd96	equ	0eh		; 9600 baud rate	8
bd19k	equ	0fh		; 19.2k baud rate	9


;Values for control reg for each baud rate: 0 if invalid
brval:
;		 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
	db	00,01,00,00,00,07,10,12,14,15

				; <== End of your own code
	ret

brate:	db	6,0		; baud rate:
				; 0 = 110 baud	1 = 300 baud	2 = 450 baud
				; 3 = 600 baud	4 = 710 baud	5 = 1200 baud
				; 6 = 2400 baud 7 = 4800 baud	8 = 9600 baud
				; 9 = 19200 baud
parity:	db	'N',0		; parity (will be 'N', 'E' or 'O')
data:	db	8,0		; data bits (will be 7 or 8)
stop:	db	1,0		; stop bits (will be 1 or 2)
reg5:	db	reg5ins

;
; Set the port. ZMP supplies either 0 or 1 as a parameter.
;
setport:
	ld	hl,2		; get port number
	add	hl,sp
	ex	de,hl
	call	getparm		; in HL (values are 0 and 1)

				; <== Insert your own code here

				; <== End of your own code
	ret
;****************************************************************************
;Video terminal sequences: these are for ADM-3A: Modify as you wish
;Cursor addressing:
cursadd:
	ld	hl,2		; get parameters
	add	hl,sp
	ex	de,hl
	call	getparm		; in HL
	ld	(row),hl	; row
	call	getparm
	ld	(col),hl	; column
				; <== Insert your own code here
				; using values in row and col
	call	print
	db	esc,'=',0	; ADM-3A leadin
	ld	a,(row)		; row first
	add	a,' '		; add offset
	call	cout
	ld	a,(col)		; sane for column
	add	a,' '
	call	cout
				; <== end of your own code
	ret

row:	ds	2		; row
col:	ds	2		; column


;Clear screen:
cls:
	call	print
	db	01ah,0
	ret

;Inverse video on:
invon:
	call	print
	db	esc,'B','0',esc,'B','1',0
	ret

;Inverse video off:
invoff:
	call	print
	db	esc,'C','0',esc,'C','1',0
	ret

;Turn off cursor:
hide:
	call	print
	db	esc,'C','4',0
	ret

;Turn on cursor:
show:
	call	print
	db	esc,'B','4',0
	ret

;Save cursor position:
savecu:
	call	print
	defb	esc,'B','6',0
	ret

;Restore cursor position:
rescu:
	call	print
	defb	esc,'C','6',0
	ret

;****************************************************************************

;Service modem interrupt:
mint:
	ret			; my system doesn't need this

;Initialise interrupt vectors:
invec:
	ret			; ditto

;De-initialise interrupt vectors:
dinvec:
	ret			; ditto

;****************** End of user-defined code ********************************
; Don't change anything below this point. We needed some assembly language
; stuff for speed, and this seemed like a good place to put it.

;Modem character test for 100 ms
mrd:
	push	bc		; save bc
	ld	bc,100		; set limit
mrd1:
	call	mirdy		; character at modem?
	jr	nz,mrd2		; yes, exit
	ld	hl,1		; else wait 1ms
	call	waitms
	dec	bc		; loop till done
	ld	a,b
	or	c
	jr	nz,mrd1
	ld	hl,0		; none there, result=0
	xor	a
mrd2:
	pop	bc
	ret

; Inline print routine: destroys A and HL

print:
	ex	(sp),hl		; get address of string
ploop:
	ld	a,(hl)		; get next
	inc	hl		; bump pointer
	or	a		; done if zero
	jr	z,pdone
	call	cout		; else print
	jr	ploop		; and loop
pdone:
	ex	(sp),hl		; restore return address
	ret			; and quit

;
;Output a character in A to the console
;
cout:
	push	bc		; save regs
	push	de
	push	hl
	ld	e,a		; Save the character to E for BDOS routine
	ld	c,2		; BDOS conout routine
	call	bdos		; print it
	pop	hl
	pop	de
	pop	bc
	ret

;Wait(seconds)
wait:
	ld	hl,2
	add	hl,sp
	ex	de,hl		; get delay size
	call	getparm
				; fall thru to..
;Wait seconds in HL
waits:
	push	bc		; save bc
	push	de		; de
	push	ix		; and ix
	ld	ix,0		; then point ix to 0
				; so we don't upset memory-mapped i/o

;Calculate values for loop constants. Need to have two loops to avoid
;   16-bit overflow with clock speeds above 9 MHz.

outerval	equ	(clkspd	/ 10) +	1
innerval	equ	(6667 /	outerval) * clkspd

wait10:
	ld	b,outerval

wait11:
	ld	de,innerval

wait12:
	bit	0,(ix)		; time-wasters
	bit	0,(ix)
	bit	0,(ix)		; 20 T-states each
	bit	0,(ix)
	bit	0,(ix)
	bit	0,(ix)
	dec	de
	ld	a,e
	ld	a,d
	or	e
	jr	nz,wait12	; 150 T-states per inner loop
	djnz	wait11		; decrement outer loop
	dec	hl		; ok, decrement count in hl
	ld	a,h
	or	l
	jr	nz,wait10
	pop	ix		; done -- restore ix
	pop	de		; de
	pop	bc		; and bc
	ret

;Wait milliseconds
mswait:
	ld	hl,2
	add	hl,sp
	ex	de,hl		; get delay size
	call	getparm
				; fall thru to..
;Wait milliseconds in HL
waitms:
	push	de
w1ms0:
	ld	de,39 *	clkspd
w1ms1:
	dec	de
	ld	a,d
	or	e
	jr	nz,w1ms1
	dec	hl
	ld	a,h
	or	l
	jr	nz,w1ms0
	pop	de
	ret

;Get next parameter from (de) into hl
getparm:
	ex	de,hl		; get address into hl
	ld	e,(hl)		; get lo
	inc	hl
	ld	d,(hl)		; then hi
	inc	hl		; bump for next
	ex	de,hl		; result in hl, address still in de
	ret

;Get address of user-defined variables

getvars:
	ld	hl,uservars
	ret

uservars:
	dw	overdrive	; .OVR etc. drive/user
	dw	overuser


	 if	($ - codebgn) gt ovsize
toobig:	jp	errval		; Overlay too large!
	 endif

	end
